<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>小皮咖数独</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <script
      type="text/javascript"
      src="https://vuejs.org/js/vue.min.js"
    ></script>
    <style>
      body,
      h1,
      h2,
      h3,
      h4,
      h5,
      h6,
      dl,
      dt,
      dd,
      ul,
      ol,
      li,
      th,
      td,
      p,
      pre,
      form,
      input,
      button,
      textarea,
      hr {
        margin: 0;
        padding: 0;
      }

      em,
      i {
        font-style: normal;
      }

      ul,
      li {
        list-style-type: none;
      }
      .clear:after,
      .clearfix:after {
        display: block;
        content: "";
        clear: both;
      }
      .fllil li {
        float: left;
      }
      .ml30 {
        margin-left: 30px;
      }
      li {
        list-style-type: none;
      }
      .shake {
        animation: shake-opacity 800ms 1 ease-in-out;
      }
      @keyframes shake-opacity {
        0% {
          transform: translate(0px, 0px) rotate(0deg);
          opacity: 0.6;
        }
        10% {
          transform: translate(-2px, -1px) rotate(-0.5deg);
          opacity: 0.5;
        }
        20% {
          transform: translate(-4px, 4px) rotate(1.5deg);
          opacity: 0.4;
        }
        30% {
          transform: translate(-4px, -1px) rotate(-1.5deg);
          opacity: 0.8;
        }
        40% {
          transform: translate(-2px, -1px) rotate(-2.5deg);
          opacity: 0.3;
        }
        50% {
          transform: translate(-4px, 1px) rotate(-2.5deg);
          opacity: 0.5;
        }
        60% {
          transform: translate(-2px, 4px) rotate(0.5deg);
          opacity: 0.1;
        }
        70% {
          transform: translate(-3px, 1px) rotate(-0.5deg);
          opacity: 0.4;
        }
        80% {
          transform: translate(0px, 0px) rotate(-0.5deg);
          opacity: 0.5;
        }
        90% {
          transform: translate(2px, -1px) rotate(-2.5deg);
          opacity: 0.8;
        }
      }
      .num-box {
        margin: 0 auto;
        width: 540px;
        position: relative;
      }
      .num-box .num-check {
        position: absolute;
        width: 180px;
        box-shadow: 0 0 10px 0 #000;
        left: 0;
        top: 0;
        transition: all 0.3s;
      }
      .num-box .num-check li {
        box-sizing: border-box;
        float: left;
        background: #fff;
        color: #58b7ff;
        width: 60px;
        height: 60px;
        text-align: center;
        line-height: 60px;
        font-size: 24px;
        border: 1px solid #58b7ff;
        cursor: pointer;
        transition: all 0.5s;
      }
      .num-box .num-check li:hover {
        color: #fff;
        background: #58b7ff;
        border: 1px solid #fff;
      }
      .num-tips {
        color: #333;
        line-height: 32px;
        font-size: 20px;
        padding: 16px;
      }
      .num-table {
        position: relative;
      }
      .num-row {
        font-size: 0;
      }
      .num-row:hover .num-col,
      .num-row:hover .num-col.no,
      .num-row:hover .num-col.cur-col {
        background: #0068b7;
      }
      .num-row .num-col {
        width: 60px;
        height: 60px;
        line-height: 60px;
        float: left;
        box-sizing: border-box;
        text-align: center;
        background: #58b7ff;
        color: #fff;
        font-size: 24px;
        font-weight: bold;
        border: 1px solid #ccc;
      }
      .num-row .num-col.no {
        background: #ccc;
        border: 1px solid #fff;
      }
      .num-row .num-col.err {
        color: #ff4949;
      }
      .num-row .num-col.cur-col {
        background: #0068b7;
      }
      .num-row .num-col.cur {
        background: #fff !important;
      }
    </style>
  </head>
  <body>
    <div class="num-box" v-show="numShow" id="num">
      <div class="num-tips">
        <p>所谓的数独：规则</p>
        <p>1.每一行数字不重复</p>
        <p>2.每一列数字不重复</p>
      </div>
      <div
        class="num-table"
        @mouseleave="hoverCol=''"
        :class="{'shake':isShake}"
      >
        <!--遍历每一行-->
        <div v-for="row,index in allNum" class="num-row clear">
          <!--遍历行里面的每一列-->
          <!--
                no:被掏空数组的样式
                cur:格子被点击时触发，被点击的格子样式
                cur-col:鼠标进入的时候触发，和被点击格子同一列的格子的样式
                err：填写错误的时候触发的样式
            -->
          <div
            v-for="num1,indexSub in row"
            :class="{'no':num1==='',
                 'cur':curRow===index&&indexSub===curCol,
                 'cur-col':hoverCol===indexSub,
                 'err':(optionNow.x===index&&optionNow.y===indexSub)||(optionNowInRow.x===index&&optionNowInRow.y===indexSub)||(optionNowInCol.x===index&&optionNowInCol.y===indexSub)}"
            @click="showCheck(index,indexSub)"
            @mouseenter="hoverCol=indexSub;"
            class="num-col"
          >
            {{allNumText[index][indexSub]}}
          </div>
        </div>
        <!--数字键盘-->
        <div
          class="num-check clear"
          :style="{'top':(curRow+1)*60+'px','left':(keyBoardLeft+1)*60+'px'}"
          v-show="checkShow"
        >
          <ul>
            <li @click="inputText(1)">1</li>
            <li @click="inputText(2)">2</li>
            <li @click="inputText(3)">3</li>
            <li @click="inputText(4)">4</li>
            <li @click="inputText(5)">5</li>
            <li @click="inputText(6)">6</li>
            <li @click="inputText(7)">7</li>
            <li @click="inputText(8)">8</li>
            <li @click="inputText(9)">9</li>
          </ul>
        </div>
      </div>
    </div>
  </body>
  <script>
    new Vue({
      el: "#num",
      data: {
        name: "welcome",
        testText: "欢迎来到",
        nowIndex: 0,
        allNum: [], //数字排列
        answer: [], //所有答案的坐标点
        allNumText: [], //数字，包括输入后的数字
        curRow: "", //当前格子所在的行的索引
        curCol: "", //当前格子所在的列的索引
        checkShow: false, //数字键盘的显示
        hoverCol: "", //鼠标进去的当前列
        hoverRow: 0, //鼠标进入的当前行
        numShow: true, //数独的显示
        optionNow: {}, //输入后的格子的坐标
        optionNowInRow: {}, //和输入后的格子在同一行，并且同样值的格子的坐标
        optionNowInCol: {}, //和输入后的格子在同一列，并且同样值的格子的坐标
        isErr: false, //是否输入错误后
        isShake: false //是否显示震动的样式
      },
      computed: {
        keyBoardLeft() {
          if (this.curCol === "") return "";
          else if (this.curCol > 5) return this.curCol - 4;
          else return this.curCol;
        }
      },
      methods: {
        /**
         * @description 显示数字键盘
         * @param i1
         * @param i2
         */
        showCheck(i1, i2) {
          //点击的格子是否是被掏空的格子
          if (this.allNum[i1][i2] !== "") {
            this.checkShow = false;
            return;
          }
          //点击的格子如果是上一次点击的格子（当前格子）
          if (i1 === this.curRow && i2 === this.curCol) {
            //隐藏数字键盘，curRow和curCol设空
            this.checkShow = false;
            this.curRow = "";
            this.curCol = "";
          } else {
            //隐藏数字键盘，curRow和curCol分别设置成当前的点
            this.checkShow = true;
            this.curRow = i1;
            this.curCol = i2;
          }
        },
        inputText(_text) {
          //*****************************检查前的初始化
          let _row = this.curRow,
            _col = this.curCol;
          this.curRow = "";
          this.curCol = "";
          this.isErr = false;
          this.optionNow = {
            x: "",
            y: ""
          };
          this.optionNowInRow = {
            x: "",
            y: ""
          };
          this.optionNowInCol = {
            x: "",
            y: ""
          };
          //*****************************检查行
          //保存当前格子的值
          this.allNumText[_row][_col] = _text;
          let rowCheck = Object.assign(this.allNumText[_row], []);
          this.checkShow = false;
          for (let i = 0, len = rowCheck.length; i < len; i++) {
            //如果值一样，但是坐标不一样，就是填写错误
            if (_text === rowCheck[i] && _col !== i) {
              this.isErr = true;
              this.isShake = true;
              //记录当前格子的信息
              this.optionNow = {
                x: _row,
                y: _col
              };
              //记录和当前格子同一行，以及同一个值的格子的坐标
              this.optionNowInRow = {
                x: _row,
                y: i
              };
            }
          }
          //*****************************检查列
          let colCheck = [];
          //首先把每一行的那一列的数值保存起来
          for (let i = 0, len = this.allNumText.length; i < len; i++) {
            colCheck.push(this.allNumText[i][_col]);
          }
          //遍历检查
          for (let i = 0, len = colCheck.length; i < len; i++) {
            //如果值一样，但是坐标不一样，就是填写错误
            if (_text === colCheck[i] && _row !== i) {
              this.isErr = true;
              this.isShake = true;
              //记录和当前格子同一列，以及同一个值的格子的坐标
              this.optionNowInCol = {
                x: i,
                y: _col
              };
            }
          }
          //如果发现的同样的
          if (this.isErr) {
            setTimeout(() => {
              this.isShake = false;
            }, 200);
            return;
          }
          //如果数组去重后，长度小于9，就是行没完成
          rowCheck = rowCheck.filter(item => item !== "");
          if (rowCheck.length !== 9) {
            console.log("行没完成");
            return;
          }

          let coloCheck = [];
          //如果数组去重后，长度小于9，就是列没完成
          for (let i = 0, len = this.allNumText.length; i < len; i++) {
            coloCheck = [...new Set(this.allNumText[i])];
            coloCheck = coloCheck.filter(item => item !== "");
            if (coloCheck.length !== 9) {
              console.log("没完成");
              return;
            }
          }
          alert("挑战成功，但是没奖品");
          this.numShow = false;
        }
      },
      mounted() {
        let arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9];
        let row = [],
          rowCol = 0;
        for (let i = 0, len = arr1.length; i < len; i++) {
          row = Object.assign([], arr1);
          this.allNum.push(row);
          rowCol = arr1.splice(0, 1)[0];
          arr1.push(rowCol);
        }
        //打乱行
        this.allNum.sort((n1, n2) => Math.random() - 0.5);
        //随机获取两列的索引
        function randomText() {
          let rondomIndex = 0,
            rondomIndexAfter = 0;
          //获取第一列的索引
          rondomIndex = Math.floor(Math.random() * 9);
          function randomDo() {
            rondomIndexAfter = Math.floor(Math.random() * 9);
            //如果第一列和第二列索引一样，第二列的索引再次重新获取
            if (rondomIndexAfter === rondomIndex) {
              randomDo();
            }
          }

          randomDo();
          //返回两列的索引
          return [rondomIndex, rondomIndexAfter];
        }

        //打乱列
        let randomArr = [],
          nowValue = 0;
        //同样遍历9次
        for (let i = 0; i < 9; i++) {
          randomArr = Object.assign([], randomText());
          //遍历每一行，给每一行的随机两列交换值
          for (let j = 0, len = this.allNum.length; j < len; j++) {
            //随机两列交换值
            nowValue = this.allNum[j][randomArr[0]];
            this.allNum[j][randomArr[0]] = this.allNum[j][randomArr[1]];
            this.allNum[j][randomArr[1]] = nowValue;
          }
        }

        //记录所有坐标
        let rowText = "",
          arrText = [];
        for (let i = 0; i < 9; i++) {
          rowText = "";
          for (let j = 0; j < 9; j++) {
            rowText += i + "-" + j + ",";
          }
          arrText.push(rowText.substr(0, rowText.length - 1));
        }
        console.log(arrText);
        //随机掏空
        let nowItme = [],
          _option,
          nowOption = [];
        for (let i = 0; i < 9; i++) {
          //抽取当前行的所有坐标
          nowItme = arrText[i].split(",");
          nowOption = [];
          //当前行的随机两个坐标掏空
          for (let j = 0; j < 2; j++) {
            //抽取当前行的随机一个坐标
            _option = Math.floor(Math.random() * nowItme.length);
            //分割坐标的x,y
            nowOption = nowItme.splice(_option, 1)[0].split("-");
            this.allNum[nowOption[0]][nowOption[1]] = "";
          }
        }
        //深度拷贝数独的数字
        this.allNumText = JSON.parse(JSON.stringify(this.allNum));
      }
    });
  </script>
</html>
